/**
 *
 * © Bahadir Zeren 2011
 * bzeren@gmail.com
 * No warranties, express or implied, are made for this program.
 *
 */
package org.baatar.opt.genetic.set.covering.binary;

import java.util.ArrayList;
import java.util.BitSet;
import org.baatar.opt.genetic.chromosome.IChromosome;
import org.baatar.opt.genetic.chromosome.MigrantChromosome;
import org.baatar.opt.genetic.set.meta.Column;
import org.baatar.opt.genetic.set.meta.DataSet;
import org.baatar.opt.genetic.set.meta.Row;
import org.baatar.util.RandomUtil;

/**
 * 
 * 
 * @author B_ZEREN
 */
public final class SCChromosome implements IChromosome<Boolean, BitSet>, Cloneable {

    private double fitness = 0.0;

    private int numOfCoveredRows = 0;
    private int numOfOverCoverings = 0;
    private int numOfColumnsInSolution = 0;
    private DataSet dataSet = null;

    protected byte colCount_Row[] = null;
    private BitSet genes = null;
    protected int chromosomeLength = 0;

    private boolean migrant = false;

    public SCChromosome(DataSet dataSet) {
        this.dataSet = dataSet;
        this.genes = new BitSet(dataSet.columns.length);
        this.colCount_Row = new byte[dataSet.rows.length];
        this.chromosomeLength = dataSet.columns.length;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        SCChromosome chromosome = (SCChromosome) super.clone();
        chromosome.colCount_Row = colCount_Row.clone();
        chromosome.genes = (BitSet) genes.clone();
        chromosome.migrant = false;
        return chromosome;
    }

    @Override
    public void initializeChromosome(RandomUtil random) {

        int numOfRows = dataSet.rows.length;

        Row row = null;
        int numOfColsOfRow = 0;
        int rowColumnIndex = 0;
        Column col = null;

        for (int i = 0; i < numOfRows; i++) {

            row = dataSet.rows[i];

            if (row.columns != null) {  //  If there is a column for the row.

                if (!(colCount_Row[row.ndx] > 0)) {

                    numOfColsOfRow = row.columns.length;

                    if (numOfColsOfRow > 0) {

                        rowColumnIndex = random.getRandNumber(numOfColsOfRow);

                        col = row.columns[rowColumnIndex];

                        setGeneValue(col.ndx, true);
                    }
                }
            }
        }
        this.migrant = false;
    }

    @Override
    public void initializeChromosome(BitSet migrantGenes) {
        for (int i = 0; i < chromosomeLength; i++) {
            if (migrantGenes.get(i))
                setGeneValue(i, true);
        }
        this.migrant = true;
    }

    @Override
    public boolean isEqual(IChromosome<Boolean, BitSet> ch) {
        SCChromosome chrom = (SCChromosome) ch;
        if (this.numOfCoveredRows != chrom.numOfCoveredRows) {
            return false;
        }
        if (this.numOfColumnsInSolution != chrom.numOfColumnsInSolution) {
            return false;
        }
        for (int i = 0; i < chromosomeLength; i++) {
            if (this.genes.get(i) != chrom.genes.get(i)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public ArrayList<Column> getSolution() {
        ArrayList<Column> resultList = new ArrayList<Column>();
        for (int i = 0; i < chromosomeLength; i++) {
            if (genes.get(i)) {
System.out.println(i + " - " + dataSet.columns[i].cost);
                resultList.add(dataSet.columns[i]);
            }
        }
        return resultList;
    }

    @Override
    public double getFitness() {
        return fitness;
    }

    @Override
    public String toString() {
        return 
        "noc = " + String.valueOf(this.numOfColumnsInSolution) + ", " +
        "nocr = " + String.valueOf(this.numOfCoveredRows) + ", " +
        "nooc = " + String.valueOf(this.numOfOverCoverings) + ", " +
        "c = " + String.valueOf(Math.floor(this.getFitness()));
    }

    @Override
    public int getChromosomeLength() {
        return dataSet.columns.length;
    }

    @Override
    public void calculateFitness() {
    }

    @Override
    public Boolean getGeneValue(int index) {
        return genes.get(index);
    }

    @Override
    public void setGeneValue(int index, Boolean value) {

        if (value) {
            if (!genes.get(index)) {

                genes.set(index);

                Column column = dataSet.columns[index];

                numOfCoveredRows += column.rows.length;
                numOfColumnsInSolution++;

                fitness += column.cost;
                fitness += column.layoverCost;

                int pflSize = column.rows.length;
                Row row = null;
                for (int i = 0; i < pflSize; i++) {
                    row = column.rows[i];

                    if (colCount_Row[row.ndx] >= 1) {
                        fitness += colCount_Row[row.ndx] * row.cost * 5;
                        numOfOverCoverings++;
                    }
                    colCount_Row[row.ndx]++;
                }
            }
        } else {
            if (genes.get(index)) {

                genes.set(index, false);

                Column column = dataSet.columns[index];

                numOfCoveredRows -= column.rows.length;
                numOfColumnsInSolution--;

                fitness -= column.cost;
                fitness -= column.layoverCost;

                int pflSize = column.rows.length;
                Row row = null;
                for (int i = 0; i < pflSize; i++) {
                    row = column.rows[i];

                    colCount_Row[row.ndx]--;
                    if (colCount_Row[row.ndx] >= 1) {
                        fitness -= colCount_Row[row.ndx] * row.cost * 5;
                        numOfOverCoverings--;
                    }
                }
            }
        }
    }

    @Override
    public MigrantChromosome<BitSet> extractMigrant(int clusterIndex, int optimizerIndex) {
        return new MigrantChromosome<BitSet>(clusterIndex, optimizerIndex, fitness, genes);
    }

    @Override
    public BitSet getGenes() {
        return genes;
    }

    @Override
    public boolean isMigrant() {
        return migrant;
    }

    @Override
    public void revertFitness() {
    }
}
